﻿Imports System.Reflection
Imports System.Collections.ObjectModel
Imports Kairyu.Load

Namespace Save

    ''' <summary>
    ''' リストアーカイブ
    ''' </summary>
    ''' <remarks>
    ''' #107 Storageクラスの見直し
    ''' 旧テーブルコンテナクラス
    ''' </remarks>
    Public Class ListArchive
        Implements ISaveDefinitionFactory, IArchive

#Region "プロパティ"
        Private _propertyName As String
        ''' <summary>
        ''' コレクションプロパティ名
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property PropertyName As String
            Get
                Return _propertyName
            End Get
            Friend Set(value As String)
                _propertyName = value
            End Set
        End Property

        Public ReadOnly Property Key As String Implements IArchive.Key
            Get
                Return _propertyName
            End Get
        End Property

        '#107 Storageクラスの見直し
        Private _tableArchives As Dictionary(Of String, TableArchive)
        Public ReadOnly Property TableArchives As Dictionary(Of String, TableArchive)
            Get
                If _tableArchives Is Nothing Then _tableArchives = New Dictionary(Of String, TableArchive)
                Return _tableArchives
            End Get
        End Property
#End Region

        ''' <summary>
        ''' ダーティかどうかを示します
        ''' </summary>
        ''' <param name="instance">検査対象インスタンス</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function IsDirty(instance As Object) As Boolean Implements IArchive.IsDirty
            'コレクションに補正
            Dim lst As ICollection = Nothing
            If TypeOf instance Is ICollection Then
                lst = DirectCast(instance, ICollection)
            Else
                lst = {instance}
            End If

            '現在のコレクション値を辞書化
            Dim actuals = Me.CreateInstanceDictionary(lst)

            '追加ダーティ（現在は存在するが、アーカイブに存在しない）
            If Me.GetAddInstances(actuals).Any Then Return True

            '削除ダーティ（アーカイブに存在するが、現在は存在しない）
            If Me.GetDeleteArchives(actuals).Any Then Return True

            '更新ダーティ（アーカイブされている場合、アーカイブに判定を依頼する）
            If Me.IsUpdate(actuals) Then Return True

            Return False
        End Function

        Protected Function CreateInstanceDictionary(instances As ICollection) As Dictionary(Of String, Object)
            Dim dic As New Dictionary(Of String, Object)
            For Each item In instances
                Dim key As String = ModelKeyFactory.Create(item)
                dic.Add(key, item)
            Next
            Return dic
        End Function

#Region "ISaveDefinitionFactory 実装"
        ''' <summary>
        ''' 保存定義リストを生成します
        ''' </summary>
        ''' <param name="instance">単一インスタンス</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateSaveDefinitions(instance As Object) As List(Of SaveDefinition) Implements ISaveDefinitionFactory.CreateSaveDefinitions
            If TypeOf instance Is ICollection Then
                Return Me.CreateSaveDefinitionsCore(DirectCast(instance, ICollection))
            Else
                Return Me.CreateSaveDefinitionsCore({instance})
            End If
        End Function

        ''' <summary>
        ''' 強制的に保存定義リストを生成します（保存タイプは任意指定）
        ''' </summary>
        ''' <param name="instance">単一インスタンス</param>
        ''' <param name="saveType">保存タイプ。追加、または削除を指定します。</param>
        ''' <returns></returns>
        ''' <remarks>
        ''' アーカイブにしか存在しないものは強制的に削除判定になったり、
        ''' アーカイブには存在しないものは強制的に追加版手になる
        ''' </remarks>
        Public Function CreateSaveDefinitions(instance As Object, saveType As SaveType) As List(Of SaveDefinition) Implements ISaveDefinitionFactory.CreateSaveDefinitions
            If TypeOf instance Is ICollection Then
                Return Me.CreateSaveDefinitionsCore(DirectCast(instance, ICollection), saveType)
            Else
                Return Me.CreateSaveDefinitionsCore({instance}, saveType)
            End If
        End Function

        ''' <summary>
        ''' コレクションインスタンスから保存定義リストを生成します
        ''' </summary>
        ''' <param name="instances">コレクションインスタンス</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateSaveDefinitionsCore(instances As ICollection) As List(Of SaveDefinition)
            Dim lst As New List(Of SaveDefinition)

            '現在のコレクション値を辞書化
            Dim actuals = Me.CreateInstanceDictionary(instances)

            '追加ダーティ（配下はすべて強制的に追加ダーティとして扱います）
            For Each item In Me.GetAddInstances(actuals)
                Dim arch As TableArchive = TableArchiveFactory.Create(item.Value)
                lst.AddRange(arch.CreateSaveDefinitions(item.Value, SaveType.Create))
            Next

            '削除ダーティ（配下はすべて強制的に削除ダーティとして扱います）
            For Each item In Me.GetDeleteArchives(actuals)
                lst.AddRange(item.CreateSaveDefinitions(item, SaveType.Delete))
            Next

            '更新ダーティ（ダーティ判定はここではしません。）
            For Each item In (From el In Me.TableArchives Where (From x In actuals Select x.Key).Contains(el.Key) Select el.Value)
                lst.AddRange(item.CreateSaveDefinitions(actuals(item.Key)))
            Next

            Return lst
        End Function

        ''' <summary>
        ''' コレクションインスタンスから強制的に保存定義リストを生成します（保存タイプは任意指定）
        ''' </summary>
        ''' <param name="instances">コレクションインスタンス</param>
        ''' <param name="saveType">
        ''' 保存タイプ
        ''' 追加、または削除を指定します</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateSaveDefinitionsCore(instances As ICollection, saveType As SaveType) As System.Collections.Generic.List(Of SaveDefinition)
            Dim lst As New List(Of SaveDefinition)

            If TypeOf instances Is ILazy = False OrElse DirectCast(instances, ILazy).IsFetched Then
                For Each item In instances
                    Dim arch As TableArchive = TableArchiveFactory.Create(item)
                    lst.AddRange(arch.CreateSaveDefinitions(item, saveType))
                Next
            End If

            Return lst
        End Function
#End Region

#Region "クエリ"
        ''' <summary>
        ''' 追加されたインスタンスを取得します
        ''' </summary>
        ''' <param name="actuals"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Friend Function GetAddInstances(actuals As Dictionary(Of String, Object)) As IEnumerable(Of Object)
            Dim q = (From el In actuals Where Not Me.TableArchives.ContainsKey(el.Key) Select el.Value)
            Return q
        End Function

        ''' <summary>
        ''' 削除されたアーカイブを取得します
        ''' </summary>
        ''' <param name="actuals"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Friend Function GetDeleteArchives(actuals As Dictionary(Of String, Object)) As IEnumerable(Of TableArchive)
            Dim q = (From el In Me.TableArchives Where Not actuals.Keys.Contains(el.Key) Select el.Value)
            Return q
        End Function

        ''' <summary>
        ''' 更新されたアーカイブを取得します
        ''' </summary>
        ''' <param name="actuals"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Friend Function IsUpdate(actuals As Dictionary(Of String, Object)) As Boolean
            'キーが一致したテーブルアーカイブでダーティチェックをする
            For Each arch In (From el In Me.TableArchives Where actuals.ContainsKey(el.Key) Select el.Value)
                Dim c = actuals(arch.Key)
                If arch.IsDirty(c) Then Return True
            Next
            Return False
        End Function
#End Region

    End Class

End Namespace
